8

GraphQL 渐进学习 09-graphql-apollo-client-vue-客户端开发

软件环境

  • vue > 2.5.x

目标

  • 创建 graphql 客户端
  • 封装请求处理
  • 基于 token 验证

代码

步骤

1 安装依赖包

  • npm install
npm install --save vue-apollo graphql apollo-client apollo-link apollo-link-http apollo-cache-inmemory graphql-tag
  • webpack.json 包清单
  "dependencies": {
    "apollo-cache-inmemory": "^1.2.0",
    "apollo-client": "^2.3.0",
    "apollo-link": "^1.2.2",
    "apollo-link-http": "^1.5.4",
    "element-ui": "^2.3.7",
    "graphql": "^0.13.2",
    "graphql-tag": "^2.9.2",
    "vue": "^2.5.2",
    "vue-apollo": "^3.0.0-beta.5",
    "vue-router": "^3.0.1"
  },
如果遇到代码编译问题,请对照我使用的包版本

2 编写 webpack 配置

  • 文件 build/webpack.base.conf.js
  module: {
    rules: [
      ...
      {
        test: /\.(graphql|gql)$/,
        exclude: /node_modules/,
        loader: 'graphql-tag/loader'
      }
      ...
    ]
  }
  • import 时,自动做 schema js 包装
  • import QUERY_USER from '@/graphql/user.graphql' ,把 QUERY_USER 打印看看

3 编写 config.json 配置

  • 文件 src/utils/config.json
{
  "graphqlServer": "http://127.0.0.1:7001/graphql",
  "tokenName": "UU_AUTH_TOKEN"
}
  • graphqlServer 服务器地址
  • tokenName 本地写 localStorage key 名称 ,推荐大家做本地化可以用 cookie 设置过期时间

4 编写 VueApollo 适配器

  • 文件 src/apolloProvider.js
import Vue from 'vue'
import {ApolloClient} from 'apollo-client'
import {HttpLink} from 'apollo-link-http'
import {InMemoryCache} from 'apollo-cache-inmemory'
import VueApollo from 'vue-apollo'
import { ApolloLink, concat, split } from 'apollo-link';
import { getMainDefinition } from 'apollo-utilities';
import Config from '@/utils/config.json'

Vue.use(VueApollo)

const httpLink = new HttpLink({
  uri: Config.graphqlServer,
})

const authMiddleware = new ApolloLink((operation, forward) => {
  const token = localStorage.getItem(Config.tokenName) || null
  operation.setContext({
    headers: {
      Authorization: `Bearer ${token}`
    }
  });
  return forward(operation);
})

const apolloClient = new ApolloClient({
  link: concat(authMiddleware, httpLink),
  cache: new InMemoryCache(),
  connectToDevTools: true,
})

const apolloProvider = new VueApollo({
  defaultClient: apolloClient,
  defaultOptions: {
    $loadingKey: 'loading'
  },
  errorHandler (error) {
    console.log('Global apollo error handler')
    console.error(error)
  }
})

export default apolloProvider
authMiddleware 中写入 Request headers token
apolloClient 中定义 cache: new InMemoryCache()
  • 文件 src/main.js
import apolloProvider from './apolloProvider'
...
new Vue({
  el: '#app',
  provide: apolloProvider.provide(),
  ...
})

5 编写 schema 定义

  • 项目中的请求定义 我都放在了 目录 src/graphql 下,有点像 restfulapi 定义
.
├── removeUser.graphql
├── user.graphql
└── users.graphql
  • 我这里写了 2 个查询 1 个更新操作,名称和服务端定义一致,这样方便查询修改

6 编写 Graphql 请求

  • 文件 src/components/HelloWorld.vue
  • 官方 readme.md 中只写了一种 组件 方式调用,我还是喜欢 api 方式
  • 代码 HelloWorld.vue
6.1 组件 方式
  apollo: {
    login: {
      query: gql`
        query queryFun($username: String!, $password: String!) {
          user(username: $username, password: $password) {
            id
            name
            token
          }
        }
      `,
      variables() {
        return {
          username: 'ducafecat',
          password: '345457yftgyhdsfghre'
        }
      },
      manual: true,
      result({data, loading}) {
        console.log(data, loading)
        if (!loading) {
          this.res = data
        }
      }
    }
  }
}
</script>

这种方式做查询,适合功能单一的展示组件,灵活性感觉还是差了点,有兴趣的可以去掉注释大家体会下。

6.2 api 方式
  • query 查询
import QUERY_USER from '@/graphql/user.graphql'
...
  methods: {
    handleLogin() {
      this.clearData()
      this.$apollo
        .query({
          // Query
          query: QUERY_USER,
          variables: {
            username: 'ducafecat',
            password: '12321321321321432'
          },
        })
        .then(response => {
          this.loading = false
          this.res = response.data
          localStorage.setItem(Config.tokenName, this.res.user.token)
          alert('登录成功,写入Token完成,重新装载 apolloProvider')
          window.location.reload()
        })
        .catch(error => {
          this.loading = false
          this.err = error
        })
    },
  • mutate 更新
    handleRemoveUser() {
      this.clearData()
      this.$apollo
        .mutate({
          // Query
          mutation: MUTATION_REMOVE_USER,
          variables: {
            id: 123
          }
        })
        .then(response => {
          this.loading = false
          this.res = response.data
        })
        .catch(error => {
          this.loading = false
          this.err = error
        })
    },

参考

1 文章

2 组件


独立开发者_猫哥
666 声望126 粉丝